using System;
using System.Collections;
using System.Collections.Generic;
using CthulhuDomain.GameEntities;
using CthulhuDomain.Services;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using System.Linq;

namespace CthulhuDomain.StageManagement
{
  public sealed class LevelStage : Stage
  {
    private double _bulletSpeed = 3f;

    #region eLinkedInfoLoaded enum

    [Flags]
    public enum eLinkedInfoLoaded : long
    {
      None = 0,
      Content = 1 << 0,
    }

    #endregion

    public LevelStage(IGameServiceProvider gameServiceManager) {
      Level = 1;
      GameServiceProvider = gameServiceManager;
      BackgroundColor = new ColorAdapter(Color.MidnightBlue);
      BackgroundColor = new ColorAdapter(Color.Black);

      PowerUps = new StagePowerUpList(gameServiceManager);
      Shoots = new StageShootList(gameServiceManager);
      Messages = new StageMessageList(GameServiceProvider);
    }

    public LevelStage(IGameServiceProvider gameServiceManager, StateManagerResult stateManagerResult, int level) {
      GameServiceProvider = gameServiceManager;

      BackgroundColor = new ColorAdapter(Color.MidnightBlue);
      BackgroundColor = new ColorAdapter(Color.Black);

      CurrentLifePoint = stateManagerResult.LifePoints;
      Level = level;
      PowerUps = new StagePowerUpList(gameServiceManager);
      Shoots = new StageShootList(gameServiceManager);
      Messages = new StageMessageList(GameServiceProvider);
    }

    public eLinkedInfoLoaded LinkedInfoLoaded { get; set; }

    private int CurrentLifePoint { get; set; }

    public override IColor BackgroundColor { get; set; }

    private DomainEntity Player { get; set; }
    private DomainEntityMatrix Army { get; set; }
    private StageMessageList Messages { get; set; }
    public StagePowerUpList PowerUps { get; set; }
    public StageShootList Shoots { get; set; }


    public double BulletSpeed {
      get { return _bulletSpeed; }
      set { _bulletSpeed = value; }
    }

    public override void Initialize() {
    }

    public override void LoadContent() {
    }

    public override StateManagerResult Update(IRectangle context, Keys[] pressedKeys, IGameTime gameTime) {
      StateManagerResult result;
      if (EnsureContent()) {
        result = ScoreManager.Update(Army, Player);

        Messages.Update(result.ResultText);
        PowerUps.Update(context);
        
        Shoots.BulletSpeed = (float)BulletSpeed;
        Shoots.Update(context);
        
        Army.Update(context, gameTime);
        Player.Update(context, pressedKeys);
      }
      else
        result = new StateManagerResult(eStateManagerResult.GameOver, 0, StateManager.CurrentScore);
      return result;
    }

    public override void Draw() {
      if (Messages != null && Messages.Count > 0)
        Messages.Draw(GameServiceProvider.SpriteBatch);

      if (PowerUps != null) PowerUps.Draw(GameServiceProvider.SpriteBatch);
      if (Shoots != null) Shoots.Draw(GameServiceProvider.SpriteBatch);
      if (Army != null) Army.Draw(GameServiceProvider.SpriteBatch);
      if (Player != null) Player.Draw(GameServiceProvider.SpriteBatch);
      ScoreManager.Draw();
    }

    private bool EnsureContent() {
      var result = false;
      if (Convert.ToBoolean(LinkedInfoLoaded & eLinkedInfoLoaded.Content)) return true;

      #region

      //var cfg = GameServiceProvider.ContentManager.LoadSetUp();

      //if (cfg.SingleOrDefault(x => x.ID == Level) != null) {
      //  var configuration = cfg.SingleOrDefault(x => x.ID == Level);

      #endregion

      if (GameServiceProvider.ContentManager.LoadSetUp().SingleOrDefault(x => x.ID == Level) != null) {
        var configuration = GameServiceProvider.ContentManager.LoadSetUp().SingleOrDefault(x => x.ID == Level);
        if (configuration != null)
          Army = new DomainEntityMatrix(configuration.Schema);

        Army.LoadContent(GameServiceProvider.ContentManager, GameServiceProvider.Context);

        var vectorAdapter = new Vector2Adapter(
          GameServiceProvider.Context.Width / 2 - DomainFactory.GetFactory(DomainFactory.eTypeEntity.Player)
                                                .CreateEntity(GameServiceProvider.ContentManager).Width / 2,
          GameServiceProvider.Context.Height - DomainFactory.GetFactory(DomainFactory.eTypeEntity.Player)
                                                .CreateEntity(GameServiceProvider.ContentManager).Height);

        Player = DomainFactory.GetFactory(DomainFactory.eTypeEntity.Player)
                              .CreateEntity(GameServiceProvider.ContentManager,
                                new PlayerState {
                                  LifePoints = CurrentLifePoint > 0 ? CurrentLifePoint : 3,
                                  StartingPosition = vectorAdapter
                                });

        ScoreManager.Initialize(GameServiceProvider);
        ScoreManager.LoadContent();
        CollisionManager.Initialize(GameServiceProvider, this);

        LinkedInfoLoaded |= eLinkedInfoLoaded.Content;
        result = true;
      }
      return result;
    }
  }


  public class StageShootList : IEnumerable
  {
    private readonly List<DomainEntity> shoots;
    protected IGameServiceProvider GameServiceProvider { get; set; }
    public int Count {
      get { return shoots.Count; }
    }

    public StageShootList(IGameServiceProvider gameServiceProvider) {
      shoots = new List<DomainEntity>();
      GameServiceProvider = gameServiceProvider;
    }

    public void Add(Shoot shoot) {
      shoots.Add(shoot);
    }

    public Shoot Get(int index) {
      return (Shoot)shoots[index];
    }

    public void Remove(Shoot shoot) {
      shoots.Remove(shoot);
    }

    protected void RemoveEntityOutOfContext() {
      shoots.RemoveAll(x => x != null && x.Position.Y <= 0);
      shoots.TrimExcess();
    }

    public void Update(IRectangle context) {
      RemoveEntityOutOfContext();

      foreach (var shoot in shoots) {
        shoot.Speed = BulletSpeed;

        shoot.Move(context);
      }
    }

    public float BulletSpeed { get; set; }

    public void Draw(ISpriteBatch spriteBatch) {
      RemoveEntityOutOfContext();

      foreach (var shoot in shoots) {
        shoot.Draw(GameServiceProvider.SpriteBatch);
      }
    }

    public IEnumerator GetEnumerator() {
      return shoots.GetEnumerator();
    }
  }

  public class StagePowerUpList : IEnumerable
  {
    private readonly List<DomainEntity> powerUps;
    protected IGameServiceProvider GameServiceProvider { get; set; }

    public int Count {
      get { return powerUps.Count; }
    }

    public StagePowerUpList(IGameServiceProvider gameServiceProvider) {
      powerUps = new List<DomainEntity>();
      GameServiceProvider = gameServiceProvider;
    }

    public void Add(PowerUp powerUp) {
      powerUps.Add(powerUp);
    }

    public PowerUp Get(int index) {
      return (PowerUp)powerUps[index];
    }

    public void Remove(PowerUp powerUp) {
      powerUps.Remove(powerUp);
    }

    protected void RemoveEntityOutOfContext() {
      if (powerUps == null) return;
      powerUps.RemoveAll(x => x != null && x.Position.Y <= 0);
      powerUps.TrimExcess();
    }

    public void Update(IRectangle context) {
      RemoveEntityOutOfContext();

      foreach (var powerUp in powerUps) {
        powerUp.Move(context);
      }
    }

    public void Draw(ISpriteBatch spriteBatch) {
      RemoveEntityOutOfContext();

      foreach (var powerUp in powerUps) {
        powerUp.Draw(GameServiceProvider.SpriteBatch);
      }
    }

    public IEnumerator GetEnumerator() {
      return powerUps.GetEnumerator();
    }

  }

  internal class StageMessageList : IEnumerable
  {
    private readonly List<StageMessage> messages;
    protected IGameServiceProvider GameServiceProvider { get; set; }

    public int Count {
      get { return messages.Count; }
    }

    public StageMessageList(IGameServiceProvider gameServiceProvider) {
      messages = new List<StageMessage>();
      GameServiceProvider = gameServiceProvider;
    }

    public void Add(StageMessage message) {
      messages.Add(message);
    }

    public void Update(string text) {
      for (int index = 0; index < messages.Count; index++) {
        var message = messages[index];
        if (message.TimeToLive > 0)
          message.Update();
        else
          messages.Remove(message);
      }

      if (!string.IsNullOrEmpty(text))
        messages.Add(new StageMessage(GameServiceProvider, text));
    }

    public IEnumerator GetEnumerator() {
      return messages.GetEnumerator();
    }

    public void Draw(ISpriteBatch spriteBatch) {
      foreach (var message in messages)
        message.Draw(spriteBatch);
    }
  }

  public class StageMessage
  {
    private readonly int X;
    private readonly int Y;
    private readonly int Degree;
    protected IGameServiceProvider GameServiceProvider { get; set; }
    protected string Text { get; set; }
    protected IGraphicResource Font { get; set; }
    protected IGraphicResource FontBig { get; set; }
    public int TimeToLive { get; set; }

    public StageMessage(IGameServiceProvider gameServiceProvider, string resultText) {
      GameServiceProvider = gameServiceProvider;
      FontBig = GameServiceProvider.ContentManager.LoadFont("ImpactBig");
      Text = resultText;
      TimeToLive = 100;
      var width = GameServiceProvider.Context.Width;
      var height = GameServiceProvider.Context.Height;
      var messageWidth = Text.Length * 10;

      X = StaticRandomNumberGenerator.RandomNext(50, width - messageWidth);
      Y = StaticRandomNumberGenerator.RandomNext(messageWidth + 30, height - messageWidth >= messageWidth + 30 ? height - messageWidth : messageWidth + 31);
      var direction = StaticRandomNumberGenerator.RandomNext(0, 100) % 2 == 0 ? 120 : -120;
      //var min = direction > 0 ? 0 : 270;
      //var max = direction > 0 ? direction : 360;
      //Degree = StaticRandomNumberGenerator.RandomNext(min, max);

      Degree = direction;
    }


    public void Draw(ISpriteBatch spriteBatch) {
      if (spriteBatch == null) return;

      spriteBatch.DrawString(FontBig, string.Format("{0}", Text),
                             new Vector2Adapter(X, Y), new ColorAdapter(Color.Red),
                             Degree, 1, 1);
    }

    public void Update() {
      TimeToLive--;
    }
  }
}